package com.sy.simpleegg.serialport.whmlyy

import android.util.Log
import com.sy.simpleegg.ext.logFile
import com.sy.simpleegg.serialport.base.BaseController
import com.sy.simpleegg.serialport.bean.ComData
import com.sy.simpleegg.serialport.util.ConvertHex
import com.sy.simpleegg.util.ByteUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.cnbleu.serialport.SerialPort
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Queue
import java.util.concurrent.LinkedBlockingDeque
import kotlin.random.Random

abstract class BaseWhmLyyController(
    portName: String,
    baudrate: SerialPort.BAUDRATE,
    stopb: SerialPort.STOPB,
    datab: SerialPort.DATAB,
    parity: SerialPort.PARITY,
    speed: Long?
) : BaseController(portName, baudrate, stopb, datab, parity, speed) {

    private val TAG = javaClass.simpleName

    private var dataHandlerJob: Job? = null
    private var dataLoop = true
    /**
     * 接收到的串口数据链表
     */
    private var dataQueue: Queue<ByteArray> = LinkedBlockingDeque()

    private val head = byteArrayOf(0xAA.toByte())
    private val end = byteArrayOf(0xDD.toByte())

    private val basicLen = 8

    private var tempHex = ""

    private var terminalConfigHex = "0100000000000000000000010001"//00 00 02 00 0000 00 0000 01 00 0100 01

    /**
     * 马达模式 0-双马达出币 1-马达1出币 2-马达2出币
     */
    private var motorModeHex = "01"

    /**
     * 马达1状态 0-空闲 1-忙碌中 2-异常 3-故障
     */
    private var motor1StateHex = "00"
    /**
     * 马达2状态 0-空闲 1-忙碌中 2-异常 3-故障
     */
    private var motor2StateHex = "00"
    /**
     * 投币器1 0-关 1-开 2-存币
     */
    private var dispenser1StateHex = "00"
    /**
     * 投币器1出币比例
     */
    private var dispenser1ProportionHex = "00"
    /**
     * 投币器2 0-关 1-开 2-存币
     */
    private var dispenser2StateHex = "00"
    /**
     * 投币器2出币比例
     */
    private var dispenser2ProportionHex = "00"
    /**
     * 纸钞机1 0-关闭 1-开启
     */
    private var banknote1StateHex = "00"
    /**
     * 纸钞机2 0-关闭 1-开启
     */
    private var banknote2StateHex = "00"
    /**
     * 现金出币比例
     */
    private var cashProportionHex = "0100"

    /**
     * 国家
     */
    private var countryHex = "01"

    private val FLAG_REC_HEART_BEAT = "01"
    private val FLAG_UPLOAD_LIST = "14"
    private val FLAG_REC_TERMINAL_CONFIG = "05"
    private val FLAG_REC_IN_INFO = "06"
    private val FLAG_RESP_TERMINAL_CONFIG = "15"
    private val FLAG_REC_OUT_INFO = "20"
    private val FLAG_REC_CASH_CONFIG = "30"
    private val FLAG_REC_MOTOR_CONFIG = "32"
    private val FLAG_REC_ONDUTY_CONFIG = "35"
    private val FLAG_REC_HISTORY_CONFIG = "37"
    private val FLAG_REC_START_KIT = "D1"
    private val FLAG_REC_SAVE = "D6"

    private var lastAmount: Long = 0
    private var lastNeedCoins: Long = 0

    init {
        openDataHandleJob()
    }

    private fun openDataHandleJob(){
        dataHandlerJob?.cancel()

        dataHandlerJob = mCoroutineScope.launch(Dispatchers.IO) {
            while (dataLoop) {
                try {
                    if (dataQueue.size > 0) {
                        var cmd: ByteArray? = null
                        while (dataQueue.poll().also { cmd = it } != null) {
                            val cmdHex = ConvertHex.byte2hex(cmd)
                            tempHex = handleSticky(tempHex + cmdHex)
                            if (tempHex.isNotEmpty()){
                                logFile("粘包数据：$tempHex")
                            }
                        }
                    }
                    delay(50L)
                } catch (e: Exception) {
                    e.printStackTrace()
                    logFile("串口数据处理异常：${e.message}")
                }
            }
        }
    }

    override fun onDataReceived(comData: ComData) {
        try {
//            val dataHex = ConvertHex.byte2hex(comData.data)
//            sticky(dataHex)
            dataQueue.add(comData.data)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("接受数据异常：${e.message}")
        }
    }

    private fun sticky(dataHex: String){
        try {
            logFile("sticky数据：$dataHex")
            if (tempHex.isEmpty()){
                if (dataHex.startsWith("AA") && dataHex.endsWith("DD") && dataHex.length >= basicLen){
                    if (dataHex.contains("DDAA")){
                        val correctDataHex = dataHex.substring(0, dataHex.indexOf("DDAA") + 2)
                        val remainDataHex = dataHex.substring(dataHex.indexOf("DDAA") + 2)
                        if (verifyDataCompleted(correctDataHex)){
                            dataHandler(correctDataHex)
                            if (remainDataHex.isNotEmpty()) {
                                sticky(remainDataHex)
                            }
                        }else{
                            tempHex = correctDataHex
                            if (remainDataHex.isNotEmpty()) {
                                sticky(remainDataHex)
                            }
                        }
                    }else{
                        //完整数据帧
                        tempHex = ""
                        dataHandler(dataHex)
                    }
                }else if (dataHex.startsWith("AA") && !dataHex.contains("DDAA")){
                    //数据帧前半段
                    tempHex = dataHex
                }else if (dataHex.startsWith("AA") && dataHex.contains("DDAA")) {
                    val correctDataHex = dataHex.substring(0, dataHex.indexOf("DDAA") + 2)
                    val remainDataHex = dataHex.substring(dataHex.indexOf("DDAA") + 2)
                    if (verifyDataCompleted(correctDataHex)){
                        dataHandler(correctDataHex)
                        if (remainDataHex.isNotEmpty()) {
                            sticky(remainDataHex)
                        }
                    }else{
                        tempHex = correctDataHex
                        if (remainDataHex.isNotEmpty()) {
                            sticky(remainDataHex)
                        }
                    }
                }else{
                    logFile("收到无效数据：$dataHex")
                    tempHex = ""
                }
            }else{
                if (dataHex == "DD" || dataHex == "DDAA"){
                    val temp = tempHex + "DD"
                    if (verifyDataCompleted(temp)){
                        tempHex = ""
                        dataHandler(temp)
                    }else{
                        tempHex += dataHex
                    }
                }
                else {
                    if (dataHex.indexOf("DD") == -1){
                        tempHex += dataHex
                    }else{
                        val tempDataHex = dataHex
//                        do {
//                            val indexSub = tempDataHex.indexOf("DD")
//                            val subHex = tempDataHex.substring(0, indexSub + 2)
//                            tempDataHex = tempDataHex.substring(indexSub + 2)
//                            tempHex += subHex
//                            if (verifyDataCompleted(tempHex)){
//                                val resultHex = tempHex
//                                tempHex = ""
//                                dataHandler(resultHex)
//                                if (tempDataHex.isNotEmpty()) {
//                                    sticky(tempDataHex)
//                                }
//                            }else{
//                                if (tempDataHex.isNotEmpty()){
//                                    tempHex += tempDataHex
//                                    if (verifyDataCompleted(tempHex)){
//                                        val resultHex = tempHex
//                                        tempHex = ""
//                                        dataHandler(resultHex)
//                                    }else{
//                                        logFile("粘包数据：检验失败")
//                                    }
//                                }
//                            }
//
//                        }while (tempDataHex.indexOf("DD") != -1)

                        val dList: MutableList<Int> = mutableListOf()
                        val size = tempDataHex.length
                        //相邻两个字符都是D的索引
                        for (i in 0 until size){
                            val firstChar = tempDataHex.substring(i, i + 1) == "D"
                            if (i + 1 < size && firstChar){
                                if (tempDataHex.substring(i + 1, i + 2) == "D"){
                                    dList.add(i)
                                    logFile("相邻两个字符都是D的索引:$i")
                                }
                            }
                        }

                        run checkData@{
                            dList.forEachIndexed() { index, dIndex ->
                                val dHex = tempDataHex.substring(0, dIndex + 2)

                                val resultHex = tempHex + dHex
                                logFile("校验第 $index 个数据:$resultHex")
                                if (verifyDataCompleted(resultHex)){
                                    dataHandler(resultHex)
                                    val remainHex = tempDataHex.substring(dIndex + 2)
                                    if (remainHex.isNotEmpty()){
                                        if (remainHex.length > 1 && remainHex.startsWith("AA")){
                                            tempHex = remainHex
                                        }else if (remainHex == "A"){
                                            tempHex = remainHex
                                        }else{
                                            tempHex = ""
                                        }
                                    }else{
                                        tempHex = ""
                                    }
                                    return@checkData
                                }
                            }
                            tempHex += tempDataHex
                            logFile("接收数据不匹配：$dataHex, 拼接后数据为：$tempHex")
                        }
                    }
                }
            }
        }catch (e: Exception){
            e.printStackTrace()
            logFile("处理数据粘包异常：${e.message}")
        }
    }

    private fun handleSticky(dataHex: String): String{
        return try {
            logFile("处理数据：$dataHex")
            if (dataHex.startsWith("AA")){
                //数据帧头正确
                if (dataHex.contains("DD")){
                    //数据帧内包含帧头帧尾，需要进一步校验数据
                    val endIndexList = getMatchWordIndex(dataHex, "DD")
                    endIndexList.forEach { endIndex ->
                        val verifyDataHex = dataHex.substring(0, endIndex + 2)
                        //校验数据长度
                        if (verifyDataLen(verifyDataHex)){
                            //符合数据帧长度，校验 校验码
                            if (verifyDataXorCode(verifyDataHex)){
                                //校验正确，处理数据帧
                                dataHandler(verifyDataHex)
                            }
                            //不管校验正确还是失败，校验结束后继续处理剩余的数据
                            val remainDataHex = dataHex.substring(endIndex + 2)
                            return if (remainDataHex.isNotEmpty()) handleSticky(remainDataHex) else ""
                        }else{
                            //不符合长度标准，进入下一个循环继续校验
                            logFile("不符合长度标准，进入下一个循环继续校验$dataHex")
                        }
                    }
                    dataHex
                }else{
                    //不包含帧尾，需要校验数据长度
                    val currentDataLen = dataHex.length
                    if (currentDataLen >= 4){
                        val dataAreaLen = dataHex.substring(2, 4).toLong(16)
                        val completeDataLen = dataAreaLen*2 + 6
                        if (currentDataLen <= completeDataLen - 2){
                            //当前数据长度 小于 完整数据长度-帧尾，说明数据不完整，等待后续拼接数据帧
                            dataHex
                        }else{
                            //当前数据既不包含帧尾AB，长度也不符合协议标准，数据异常数据，直接清空该数据不处理
                            ""
                        }
                    }else{
                        //数据长度不足，拿不到完整数据帧的长度数据，直接返回，等待后续拼接数据帧
                        dataHex
                    }
                }
            }else{
                //数据帧头异常，直接清空数据，不处理
                ""
            }
        }catch (e: Exception){
            e.printStackTrace()
            logFile("粘包数据:$dataHex 处理异常：${e.message}")
            ""
        }
    }

    /**
     * 校验数据帧的xorCode
     * @param dataHex 校验的数据帧
     */
    private fun verifyDataXorCode(dataHex: String): Boolean{
        return try {
            val xorHex = dataHex.substring(dataHex.length - 4, dataHex.length - 2)
            val checkHex = dataHex.substring(2, dataHex.length - 4)
            val verifyXorHex = ByteUtil.bytes2HexStr(byteArrayOf(ByteUtil.getXor(ByteUtil.hexStr2bytes(checkHex))))
            logFile("数据校验位=$xorHex 计算校验位=$verifyXorHex checkHex=$checkHex")
            xorHex == verifyXorHex
        }catch (e: Exception){
            e.printStackTrace()
            logFile("校验数据xorCode异常：${e.message}")
            false
        }
    }

    /**
     * 校验有数据帧头帧尾的数据长度
     * @param dataHex 数据
     */
    private fun verifyDataLen(dataHex: String): Boolean{
        return try {
            val dataLen = dataHex.length.toLong()
            if (dataLen >= 12){
                val completeAreaDataLen = dataHex.substring(2, 4).toLong(16)
                val completeDataLen = completeAreaDataLen*2 + 6
                dataLen == completeDataLen
            }else{
                false
            }
        }catch (e: Exception){
            e.printStackTrace()
            logFile("校验数据长度异常：${e.message}")
            false
        }
    }

    /**
     * 获取相同字符的index
     * @param content 内容
     * @param word 匹配字符
     */
    private fun getMatchWordIndex(content: String, word: String): MutableList<Int>{
        return try {
            if (content.isEmpty() || word.isEmpty()) return mutableListOf()
            if (content.length < word.length) return mutableListOf()
            val contentLen = content.length
            val wordLen = word.length
            var lastIndex = 0
            var tempIndex = 0

            if (content.indexOf(word) == -1){
                mutableListOf()
            }else{
                val wordIndexList: MutableList<Int> = mutableListOf()
                var temp = content
                do {
                    val index = temp.indexOf(word)

                    if (index % 2 == 0){
                        tempIndex = 0
                        wordIndexList.add(lastIndex + index)
                        lastIndex = index + wordLen
                        temp = temp.substring(lastIndex)
                    }else{
                        //index单数
                        if (index < temp.length - 1) {
                            tempIndex = index + 1
                            lastIndex = index + 1
                            temp = temp.substring(tempIndex)
                        }
                    }

//                    wordIndexList.add(lastIndex + index)
//                    lastIndex = index + wordLen
//                    temp = temp.substring(lastIndex)
                }while (temp.indexOf(word) != -1)
                wordIndexList
            }
        }catch (e: Exception){
            e.printStackTrace()
            mutableListOf<Int>()
        }
    }

    private fun dataHandler(dataHex: String){
        logFile("完整数据：$dataHex")
        val len = dataHex.substring(2, 4).toInt(16)
        val indexHex = dataHex.substring(4, 6)

        if (indexHex != "02"){
            return
        }
        val cmdHex = dataHex.substring(6, 8)
        when (cmdHex){
            FLAG_REC_HEART_BEAT -> {
                onRecHeartBeat()
            }
            FLAG_UPLOAD_LIST -> {
                //上报现金账目
                val dataArea = dataHex.substring(8, 32)
                val addCode = dataArea.substring(0, 2).toInt(16)
                val errCodeHex = dataHex.substring(2, 4)
                val cash = (dataArea.substring(6, 8) + dataArea.substring(4, 6)).toLong(16).toString()
                val outCoins = (dataArea.substring(10, 12) + dataArea.substring(8, 10)).toLong(16).toString()
                val donateCoins = (dataArea.substring(14, 16) + dataArea.substring(12, 14)).toLong(16).toString()
                val descHex = dataHex.substring(32)
                respCash()
                onRecCash(addCode, errCodeHex, cash, outCoins, donateCoins, descHex)
            }
            FLAG_REC_TERMINAL_CONFIG -> {
                //终端参数
                val dataArea = dataHex.substring(8, dataHex.length - 4)
                terminalConfigHex = dataArea

                motorModeHex = dataArea.substring(0, 2)
                motor1StateHex = dataArea.substring(2, 4)
                motor2StateHex = dataArea.substring(4, 6)
                dispenser1StateHex = dataArea.substring(6, 8)
                dispenser1ProportionHex = dataArea.substring(8, 12)
                dispenser2StateHex = dataArea.substring(12, 14)
                dispenser2ProportionHex = dataArea.substring(14, 18)
                banknote1StateHex = dataArea.substring(18, 20)
                banknote2StateHex = dataArea.substring(20, 22)
                cashProportionHex = dataArea.substring(22, 26)
                countryHex = dataArea.substring(26, 28)

                onRecTerminalConfig(dataArea)
            }
            FLAG_RESP_TERMINAL_CONFIG -> {
                //设置终端参数结果
                val result = dataHex.substring(8, 10)
                onRecSetTerminalResult(result == "01")
            }
            FLAG_REC_CASH_CONFIG -> {
                //纸钞配置参数
                val dataArea = dataHex.substring(8, dataHex.length - 4)
                onRecCashConfig(dataArea)
            }
            FLAG_REC_MOTOR_CONFIG -> {
                //马达配置参数
                val dataArea = dataHex.substring(8, dataHex.length - 4)
                onRecMotorConfig(dataArea)
            }
            FLAG_REC_START_KIT -> {
                //启动指令集
                val subCmdHex = dataHex.substring(8, 10)
                when (subCmdHex){
                    "01" -> {
                        //启动申请，查询控制板状态
                        val online = dataHex.substring(12, 14)
                        onRecPrepare(online == "01")
                    }
                    "02" -> {
                        val result = dataHex.substring(26, 28)
                        var msg = ""
                        var success = 0
                        when (result){
                            "00" -> {
                                msg = "启动失败"
                                success = 1
                            }
                            "01" -> {
                                msg = "启动正常"
                                success = 0
                            }
                            "02" -> {
                                msg = "正常启动"
                                success = 0
                            }
                            "03" -> {
                                msg = "其他故障"
                                success = 1
                            }
                        }
                        onRecStart(success, msg)
                    }
                    "03" -> {
                        var msg = "出币结果："
                        val result = dataHex.substring(26, 28)
                        val realCoins = "${dataHex.substring(30, 32)}${dataHex.substring(28, 30)}".toLong(16)
                        var success = 0
                        when (result){
                            "00" -> {
                                msg += "启动失败"
                                success = 0
                            }
                            "01" -> {
                                msg += "启动正常"
                                success = 0
                            }
                            "02" -> {
                                msg += "正常启动"
                                success = 0
                            }
                            "03" -> {
                                msg += "其他故障"
                                success = 0
                            }
                        }

                        onRecOutCoinInfo(lastAmount, lastNeedCoins, lastNeedCoins - realCoins)
                        onRecOutCoinFinish(success, msg)
                    }
                }
            }
            FLAG_REC_OUT_INFO -> {
                //出币信息
                val dataArea = dataHex.substring(8, 20)
                val amount = reverseHex(dataArea.substring(0, 4)).toLong(16)
                val needOutCoins = reverseHex(dataArea.substring(4, 8)).toLong(16)
                val remainOutCoins = reverseHex(dataArea.substring(8, 12)).toLong(16)
                lastAmount = amount
                lastNeedCoins = needOutCoins
                onRecOutCoinInfo(amount, needOutCoins, remainOutCoins)
            }
            FLAG_REC_IN_INFO -> {
                //存币信息
                val subCmd = dataHex.substring(8, 12)
                when (subCmd){
                    "0105" -> {
                        val dataAreaHex = dataHex.substring(12, dataHex.length - 4)
                        val uniqueCode = ByteUtil.hexStr2bytes(dataAreaHex.substring(0, 8)).toString()
                    }
                    else -> {
                        logFile("未知指令")
                    }
                }
            }
            FLAG_REC_SAVE -> {
                //存币指令集
                val devSubCmdHex = dataHex.substring(8, 12)

                when (devSubCmdHex){
                    //启动状态
                    "0101" -> {
                        val result = when (dataHex.substring(36, 38)){
                            "00" -> {
                                false
                            }
                            "01" -> {
                                true
                            }
                            else -> {
                                false
                            }
                        }
                        val userIdHex = dataHex.substring(46, dataHex.length - 4)
                        onSavePrepare(result, userIdHex)
                    }
                    //存币数据
                    "0105" -> {
                        val snHex = dataHex.substring(12, 28)
                        val saveCoins = (dataHex.substring(34, 36) + dataHex.substring(32, 34) + dataHex.substring(30, 32) + dataHex.substring(28, 30)).toLong(16)
                        onSaveInfo(snHex, saveCoins)
                    }
                    //主动结束存币
                    "0106" -> {
                        val snHex = dataHex.substring(12, 28)
                        val saveCoins = (dataHex.substring(34, 36) + dataHex.substring(32, 34) + dataHex.substring(30, 32) + dataHex.substring(28, 30)).toLong(16)
                        onSaveFinish(snHex, saveCoins)
                    }
                    else -> {

                    }
                }
            }
        }
    }

    /**
     * 校验数据完整性
     * @param dataHex 待校验数据
     */
    private fun verifyDataCompleted(dataHex: String): Boolean{
        return try {
            dataHex.run {
                if (isEmpty()){
                    return@run false
                }
                if (length < basicLen){
                    logFile("长度不匹配")
                    return@run false
                }
                if (!startsWith("AA")){
                    logFile("起始不为AA")
                    return@run false
                }
                if (!endsWith("DD")){
                    logFile("结尾不为DD")
                    return@run false
                }
                val lenHex = substring(2, 4)
                val len = lenHex.toInt(16)
                val dataLength = (len*2) + 6
                if (dataHex.length != dataLength){
                    logFile("长度不匹配2 len=$len dataLength=$dataLength")
                    return@run false
                }
                val xorHex = dataHex.substring(dataHex.length - 4, dataHex.length - 2)
                val checkHex = dataHex.substring(2, dataHex.length - 4)
                val verifyXorHex = ByteUtil.bytes2HexStr(byteArrayOf(ByteUtil.getXor(ByteUtil.hexStr2bytes(checkHex))))
//                logFile("数据校验位=$xorHex 计算校验位=$verifyXorHex checkHex=$checkHex")
                xorHex == verifyXorHex
            }
        }catch (e: Exception){
            e.printStackTrace()
            logFile("校验数据完整性异常：${e.message}")
            false
        }
    }

    /**
     * 回复收到现金数据
     */
    private fun respCash(){
        try {
            val preBytes = byteArrayOf(0x03, 0x01, 0x14)
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("回复收到现金数据异常：${e.message}")
        }
    }

    /**
     * 启动申请
     * @param uniqueCode 指令唯一码
     */
    protected fun transferPrepare(uniqueCode: String){
        try {
            val preBytes = byteArrayOf(0x0A, 0x01, 0xD1.toByte(), 0x01)
            val checkDataBytes = ByteUtil.combineBytes(preBytes, uniqueCode.toByteArray())
            val checkBytes = byteArrayOf(ByteUtil.getXor(checkDataBytes))
            val result = ByteUtil.combineBytes(checkDataBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("启动申请数据异常：${e.message}")
        }
    }

    /**
     * 出币
     * @param coins 出币数
     * @param amount 金额
     * @param sn 订单号
     */
    protected fun outCoin(coins: Long, amount: Long, sn: String, uniqueCode: String){
        try {
            if (coins < 1){
                logFile("出币数必须大于0")
                return
            }
            if (amount > 65000){
                logFile("金额不能超过650元")
                return
            }

            val preBytes = byteArrayOf(0x0E, 0x01, 0xD1.toByte(), 0x02)
            val coinsHex = ByteUtil.long2HexStr(coins, 4, true)
            val amountHex = ByteUtil.long2HexStr(amount, 4, true)
            val dataBytes = ByteUtil.hexStr2bytes("$amountHex$coinsHex")
            val checkDataBytes = ByteUtil.combineBytes(preBytes, uniqueCode.toByteArray(), dataBytes)
            val checkBytes = byteArrayOf(ByteUtil.getXor(checkDataBytes))
            val result = ByteUtil.combineBytes(checkDataBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("出币数据异常：${e.message}")
        }
    }

    /**
     * 设置纸钞配置
     * @param cashConfig
     */
    protected fun setCashConfig(cashConfig: String){
        try {
            val lenHex = ByteUtil.int2HexStr(3 + cashConfig.length/2, 2, false)
            val flagHex = "01"
            val cmdHex = "31"
            val preBytes = ByteUtil.hexStr2bytes("$lenHex$flagHex$cmdHex$cashConfig")
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询纸钞配置数据异常：${e.message}")
        }
    }

    /**
     * 查询终端纸钞配置参数
     */
    fun getCashConfig(){
        try {
            val preBytes = byteArrayOf(0x03, 0x01, 0x30)
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询终端纸钞配置参数异常：${e.message}")
        }
    }

    /**
     * 查询当班账目
     */
    fun getOndutyConfig(){
        try {
            val preBytes = byteArrayOf(0x03, 0x01, 0x34)
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询当班账目数据异常：${e.message}")
        }
    }

    /**
     * 设置当班账目
     * @param ondutyConfigHex 当班账目数据 00000000 00000000 00000000 00000000 00
     */
    protected fun setOndutyConfig(ondutyConfigHex: String){
        try {
            val lenHex = ByteUtil.int2HexStr(3 + ondutyConfigHex.length/2, 2, false)
            val flagHex = "01"
            val cmdHex = "35"
            val preBytes = ByteUtil.hexStr2bytes("$lenHex$flagHex$cmdHex$ondutyConfigHex")
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("操作当班账目数据异常：${e.message}")
        }
    }

    /**
     * 查询历史账目
     */
    fun getHistoryConfig(){
        try {
            val preBytes = byteArrayOf(0x03, 0x01, 0x36)
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询历史账目数据异常：${e.message}")
        }
    }

    /**
     * 设置历史账目
     * @param historyConfigHex 历史账目数据 00000000 00000000 00000000 00000000
     */
    protected fun setHistoryConfig(historyConfigHex: String){
        try {
            val lenHex = ByteUtil.int2HexStr(3 + historyConfigHex.length/2, 2, false)
            val flagHex = "01"
            val cmdHex = "37"
            val preBytes = ByteUtil.hexStr2bytes("$lenHex$flagHex$cmdHex$historyConfigHex")
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("操作历史账目数据异常：${e.message}")
        }
    }

    /**
     * 设置终端参数
     */
    fun setTerminalConfig(setDefault: Boolean = true){
        try {
            var dataAreaHex = ""
            if (setDefault){
                dataAreaHex = terminalConfigHex
            }else{

            }
            val lenHex = dataAreaHex.length/2 + 2
            val indexHex = "01"
            val cmdHex = "15"

            val preBytes = ByteUtil.hexStr2bytes("$lenHex$indexHex$cmdHex$dataAreaHex")
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询终端参数异常：${e.message}")
        }
    }

    /**
     * 设置出币模式
     * @param mode
     */
    fun setMode(mode: Int){
        motorModeHex = ByteUtil.int2HexStr(mode, 2, false)?:return
        logFile("设置出币模式：$mode")
        setDefaultParam()
        setTerminalConfig()
    }

    /**
     * 设置马达状态
     * @param motor1State
     * @param motor2State
     */
    fun setMotor(motor1State: Int, motor2State: Int){
        if (motor1State != -1){
            motor1StateHex = ByteUtil.int2HexStr(motor1State, 2, false)?:motor1StateHex
        }
        if (motor2State != -1){
            motor2StateHex = ByteUtil.int2HexStr(motor2State, 2, false)?:motor2StateHex
        }
        logFile("设置马达状态：motor1State=$motor1State motor2State=$motor2State")
        setDefaultParam()
        setTerminalConfig()
    }

    /**
     * 投币器开关
     * @param dispenser1
     * @param dispenser2
     */
    fun openDispenser(dispenser1: Boolean, dispenser2: Boolean){
        dispenser1StateHex = if (dispenser1) "01" else "00"
        dispenser2StateHex = if (dispenser2) "01" else "00"
        logFile("投币器开关：dispenser1=$dispenser1 dispenser2=$dispenser2")
        setDefaultParam()
        setTerminalConfig()
    }

    /**
     * 投币器比例
     * @param proportion1
     * @param proportion2
     */
    fun setDispenserProportion(proportion1: Int, proportion2: Int){
        if (proportion1 != -1) {
            dispenser1ProportionHex =
                ByteUtil.int2HexStr(proportion1, 4, true) ?: dispenser1ProportionHex
        }
        if (proportion2 != -1) {
            dispenser2ProportionHex =
                ByteUtil.int2HexStr(proportion2, 4, true) ?: dispenser2ProportionHex
        }
        logFile("设置投币器比例：proportion1=$proportion1 proportion2=$proportion2")
        setDefaultParam()
        setTerminalConfig()
    }

    /**
     * 设置现金出币比例
     * @param cashProportion
     */
    fun setCashProportion(cashProportion: Int){
        if (cashProportion < 1 || cashProportion > 500) return
        cashProportionHex = ByteUtil.int2HexStr(cashProportion, 4, true)?: return
        logFile("设置现金出币比例：cashProportion=$cashProportion")
        setDefaultParam()
        setTerminalConfig()
    }

    /**
     * 设置纸钞国家
     * @param country
     */
    fun setCashCountry(country: Int){
        countryHex = ByteUtil.int2HexStr(country, 2, false)?: "01"
        logFile("设置纸钞国家：country=$country")
        setDefaultParam()
        setTerminalConfig()
    }

    /**
     * 获取终端参数
     */
    fun getTerminalConfig(){
        try {
            val preBytes = byteArrayOf(0x03, 0x01, 0x05)
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询终端参数异常：${e.message}")
        }
    }

    fun setBasicConfig(configHex: String){
        motorModeHex = configHex.substring(0, 2)
        motor1StateHex = configHex.substring(2, 4)
        motor2StateHex = configHex.substring(4, 6)
        dispenser1StateHex = configHex.substring(6, 8)
        dispenser1ProportionHex = configHex.substring(8, 12)
        dispenser2StateHex = configHex.substring(12, 14)
        dispenser2ProportionHex = configHex.substring(14, 18)
        banknote1StateHex = configHex.substring(18, 20)
        banknote2StateHex = configHex.substring(20, 22)
        cashProportionHex = configHex.substring(22, 26)
        countryHex = configHex.substring(26, 28)
        terminalConfigHex = configHex
        setTerminalConfig()
    }

    /**
     * 获取马达参数
     */
    fun getMotorConfig(){
        try {
            val preBytes = byteArrayOf(0x03, 0x01, 0x32)
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询马达参数异常：${e.message}")
        }
    }

    /**
     * 设置马达参数
     */
    fun setMotorConfig(forward: Int, reverse: Int, stopDelay: Int){
        try {
            val dataHex = "${ByteUtil.int2HexStr(forward, 2, false)}${ByteUtil.int2HexStr(reverse, 2, false)}${ByteUtil.int2HexStr(stopDelay, 2, false)}"
            val lenHex = "05"
            val flagHex = "01"
            val cmdHex = "33"
            val preBytes = ByteUtil.hexStr2bytes("$lenHex$flagHex$cmdHex$dataHex")
            val checkBytes = byteArrayOf(ByteUtil.getXor(preBytes))
            val result = ByteUtil.combineBytes(preBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("设置马达参数异常：${e.message}")
        }
    }

    /**
     * 纸钞机状态设置
     * @param banknote1Open 纸钞机1 true-开启 false-关闭
     * @param banknote2Open 纸钞机2 true-开启 false-关闭
     */
    fun openBanknote(banknote1Open: Boolean, banknote2Open: Boolean){
        try {
            banknote1StateHex = if (banknote1Open) "01" else "00"
            banknote2StateHex = if (banknote2Open) "01" else "00"
            logFile("纸钞机状态设置：banknote1Open=$banknote1Open banknote2Open=$banknote2Open")
            setDefaultParam()
            setTerminalConfig()
        }catch (e: Exception){
            e.printStackTrace()
            logFile("设置纸钞机状态异常：${e.message}")
        }
    }

    /**
     * 开启存币
     */
    fun saveOpen(snHex: String, userIdHex: String){
        try {
            val remainder1Bytes = byteArrayOf(0x00, 0x00, 0x00, 0x00)
            val remainder2Bytes = byteArrayOf(0x00, 0x00, 0x00, 0x00)
            val lenByte = (userIdHex.length/2 + 23).toByte()
            val preBytes = byteArrayOf(lenByte, 0x01, 0xD6.toByte(), 0x01, 0x01)
            val checkDataBytes = ByteUtil.combineBytes(preBytes, ByteUtil.hexToByteArray(snHex)!!, remainder1Bytes, remainder2Bytes, byteArrayOf(lenByte), ByteUtil.hexToByteArray(userIdHex)!!)
            val checkBytes = byteArrayOf(ByteUtil.getXor(checkDataBytes))
            val result = ByteUtil.combineBytes(checkDataBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("开启存币 异常：${e.message}")
        }
    }

    /**
     * 关闭存币
     */
    protected fun saveClose(snHex: String){
        try {
            val preBytes = byteArrayOf(0x0D.toByte(), 0x02, 0xD6.toByte(), 0x01, 0x06)
            val checkDataBytes = ByteUtil.combineBytes(preBytes, ByteUtil.hexToByteArray(snHex)!!)
            val checkBytes = byteArrayOf(ByteUtil.getXor(checkDataBytes))
            val result = ByteUtil.combineBytes(checkDataBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("关闭存币 异常：${e.message}")
        }
    }

    /**
     * 查询链接状态，当作心跳用
     */
    fun checkState(){
        try {
            val idBytes = "12345678".toByteArray()
            val preBytes = byteArrayOf(0x0B, 0x01, 0x01)
            val checkDataBytes = ByteUtil.combineBytes(preBytes, idBytes)
            val checkBytes = byteArrayOf(ByteUtil.getXor(checkDataBytes))
            val result = ByteUtil.combineBytes(checkDataBytes, checkBytes)
            sendByte(result)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("查询链接状态数据异常：${e.message}")
        }
    }

    private fun setDefaultParam(){
        terminalConfigHex = "$motorModeHex$motor1StateHex$motor2StateHex$dispenser1StateHex$dispenser1ProportionHex$dispenser2StateHex$dispenser2ProportionHex$banknote1StateHex$banknote2StateHex$cashProportionHex$countryHex"
    }

    /**
     * 心跳响应
     */
    abstract fun onRecHeartBeat()

    /**
     * 收到现金上报
     * @param addCode 自增码
     * @param errCodeHex 故障代码
     * @param cash 现金面额
     * @param outCoins 出币数
     * @param donateCoins 增币数
     * @param descHex 保留数据（0x00）
     */
    abstract fun onRecCash(addCode: Int, errCodeHex: String, cash: String, outCoins: String, donateCoins: String, descHex: String)

    /**
     * 收到纸钞配置
     * @param configHex 纸钞配置
     */
    abstract fun onRecCashConfig(configHex: String)

    /**
     * 收到马达配置
     * @param configHex 马达配置
     */
    abstract fun onRecMotorConfig(configHex: String)

    /**
     * 收到控制板出币准备状态
     * @param online 控制板是否在线
     */
    abstract fun onRecPrepare(online: Boolean)

    /**
     * 控制板收到出币订单响应
     * @param success 启动状态 1-失败 0-成功
     * @param msg 启动信息
     */
    abstract fun onRecStart(success: Int, msg: String)

    /**
     * 出币结束
     * @param success 启动状态 1-失败 0-成功
     * @param msg 启动信息
     */
    abstract fun onRecOutCoinFinish(success: Int, msg: String)

    /**
     * 出币信息
     * @param amount 金额
     * @param needOutCoins 需要出币币
     * @param remainOutCoins 剩余未出币数
     */
    abstract fun onRecOutCoinInfo(amount: Long, needOutCoins: Long, remainOutCoins: Long)

    /**
     * 收到终端配置
     * @param configHex 纸钞配置
     */
    abstract fun onRecTerminalConfig(configHex: String)

    /**
     * 收到终端配置
     * @param configHex 纸钞配置
     */
    abstract fun onRecSetTerminalResult(success: Boolean)

    /**
     * 收到存币准备状态
     * @param prepare 准备状态
     */
    abstract fun onSavePrepare(prepare: Boolean, userIdHex: String)

    /**
     * 收到存币数据数据
     * @param snHex 订单号
     * @param saveCoins 存币数
     */
    abstract fun onSaveInfo(snHex: String, saveCoins: Long)

    /**
     * 收到存币结束响应数据
     * @param snHex 订单号
     * @param saveCoins 存币数
     */
    abstract fun onSaveFinish(snHex: String, saveCoins: Long)


    /**
     * 发送数据
     */
    private fun sendByte(dataBytes: ByteArray){
        try {
//            val crcCode = CRC16.crc16Checkout(dataBytes)
            val result = ByteUtil.combineBytes(head, dataBytes, end)
            sendData(result, 0)
        }catch (e: Exception){
            logFile("sendByte 发送指令异常：${e.message}")
        }
    }



    private fun getRandom(): Int{
        return Random.nextInt(256)
    }

    private fun createSn(sn: String): String{
        return try {
            var tempSn = ByteUtil.str2HexString(sn)
            while (tempSn.length < 64){
                tempSn = "0$tempSn"
            }
            tempSn
        }catch (e: Exception){
            e.printStackTrace()
            logFile("生成货道订单号异常：${e.message}")
            var result = "0"
            while (result.length < 64){
                result = "0$result"
            }
            result
        }
    }

    fun unRepeatSixCode(): String {
        return try {
            var sixChar = ""
            val sdf = SimpleDateFormat("yyMMddHHmmss")
            val date = Date()
            val time = sdf.format(date)
            for (i in 0 until time.length / 2) {
                var singleChar: String
                val x = time.substring(i * 2, (i + 1) * 2)
                val b = x.toInt()
                singleChar = if (b < 10) {
                    Integer.toHexString(x.toInt())
                } else if (b >= 10 && b < 36) {
                    (x.toInt() + 55).toChar().toString()
                } else {
                    (x.toInt() + 61).toChar().toString()
                }
                sixChar = sixChar + singleChar
            }
            Log.d(javaClass.simpleName, "生成一个6位不可重复的字符编码是：$sixChar")
            sixChar
        }catch (e: Exception){
            e.printStackTrace()
            "aaaaaa"
        }
    }

    private fun reverseHex(hexStr: String): String{
        return try {
            if (hexStr.isEmpty()){
                return ""
            }
            var tempHex = hexStr
            while (tempHex.length%2 != 0){
                tempHex += "0"
            }
            var result = ""
            for (i in tempHex.indices step 2){
                result = tempHex.substring(i, i + 2) + result
            }
            result
        }catch (e: Exception){
            e.printStackTrace()
            hexStr
        }
    }

    protected fun releaseBase(){
        dataLoop = false
    }

}